//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package user_dao

import (
	"context"
	"regexp"

	"atomgit.com/openlinksaas/api-server/dao/db_ns"
	"atomgit.com/openlinksaas/proto-gen-go.git/user_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type BasicUserInfoDbItem struct {
	DisplayName string `bson:"displayName"`
	LogoUri     string `bson:"logoUri"`
}

type FeatureInfoDbItem struct {
	EnableProject bool `bson:"enableProject"`
	EnableOrg     bool `bson:"enableOrg"`
}

type UserInfoDbItem struct {
	UserId       string              `bson:"_id"`
	UserName     string              `bson:"userName"`
	BasicInfo    BasicUserInfoDbItem `bson:"basicInfo"`
	CreateTime   int64               `bson:"createTime"`
	UpdateTime   int64               `bson:"updateTime"`
	UserFsId     string              `bson:"userFsId"`
	UserMemoFsId string              `bson:"userMemoFsId"`
	UserState    uint32              `bson:"userState"`
	UserType     uint32              `bson:"userType"`
	// DefaultKbSpaceId string              `bson:"defaultKbSpaceId"`
	TestAccount bool              `bson:"testAccount"`
	FeatureInfo FeatureInfoDbItem `bson:"featureInfo"`
}

func (dbItem *UserInfoDbItem) ToUserInfo() *user_api.UserInfo {
	return &user_api.UserInfo{
		UserId:   dbItem.UserId,
		UserName: dbItem.UserName,
		BasicInfo: &user_api.BasicUserInfo{
			DisplayName: dbItem.BasicInfo.DisplayName,
			LogoUri:     dbItem.BasicInfo.LogoUri,
		},
		CreateTime:   dbItem.CreateTime,
		UpdateTime:   dbItem.UpdateTime,
		UserFsId:     dbItem.UserFsId,
		UserMemoFsId: dbItem.UserMemoFsId,
		UserState:    user_api.USER_STATE(dbItem.UserState),
		UserType:     user_api.USER_TYPE(dbItem.UserType),
		TestAccount:  dbItem.TestAccount,
		Feature: &user_api.FeatureInfo{
			EnableProject: dbItem.FeatureInfo.EnableProject,
			EnableOrg:     dbItem.FeatureInfo.EnableOrg,
		},
	}
}

type _UserInfoDao struct {
	mc *mongo.Collection
}

func newUserInfoDao(db *mongo.Database) (*_UserInfoDao, error) {
	mc := db.Collection(db_ns.DB_NS_USER + "info")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys:    bson.M{"userName": 1},
			Options: options.Index().SetUnique(true),
		},
		{
			Keys: bson.M{"createTime": 1},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_UserInfoDao{mc: mc}, nil
}

func (db *_UserInfoDao) ExistByName(ctx context.Context, userName string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"userName": userName})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_UserInfoDao) ExistById(ctx context.Context, userId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": userId})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_UserInfoDao) Insert(ctx context.Context, dbItem *UserInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_UserInfoDao) GetByName(ctx context.Context, userName string) (*UserInfoDbItem, error) {
	retItem := &UserInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"userName": userName}).Decode(retItem)
	return retItem, err
}

func (db *_UserInfoDao) ListByName(ctx context.Context, userNameList []string) ([]*UserInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"userName": bson.M{"$in": userNameList}})
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_UserInfoDao) UpdateBasicInfo(ctx context.Context, userId string, basicDbItem *BasicUserInfoDbItem) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"basicInfo": basicDbItem}})
	return err
}

func (db *_UserInfoDao) UpdateFeatureInfo(ctx context.Context, userId string, featureDbitem *FeatureInfoDbItem) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"featureInfo": featureDbitem}})
	return err
}

func (db *_UserInfoDao) Get(ctx context.Context, userId string) (*UserInfoDbItem, error) {
	retItem := &UserInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": userId}).Decode(retItem)
	return retItem, err
}

func (db *_UserInfoDao) ListById(ctx context.Context, userIdList []string) ([]*UserInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": userIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) CountAll(ctx context.Context, filterByState bool, state uint32) (uint32, error) {
	query := bson.M{}
	if filterByState {
		query["userState"] = state
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_UserInfoDao) ListAll(ctx context.Context, filterByState bool, state uint32, offset, limit int64) ([]*UserInfoDbItem, error) {
	query := bson.M{}
	if filterByState {
		query["userState"] = state
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(bson.M{"createTime": -1}).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) CountByAdminApi(ctx context.Context,
	filterByKeyword bool, keyword string,
	filterByState bool, state uint32) (uint32, error) {
	query := bson.M{}
	if filterByKeyword {
		query["basicInfo.displayName"] = primitive.Regex{
			Pattern: regexp.QuoteMeta(keyword),
			Options: "i",
		}
	}
	if filterByState {
		query["userState"] = state
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_UserInfoDao) ListByAdminApi(ctx context.Context,
	filterByKeyword bool, keyword string,
	filterByState bool, state uint32,
	offset, limit int64) ([]*UserInfoDbItem, error) {
	query := bson.M{}
	if filterByKeyword {
		query["basicInfo.displayName"] = primitive.Regex{
			Pattern: regexp.QuoteMeta(keyword),
			Options: "i",
		}
	}
	if filterByState {
		query["userState"] = state
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(bson.M{"createTime": -1}).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) UpdateLogoUri(ctx context.Context, userId, logoUri string) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"basicInfo.logoUri": logoUri}})
	return err
}

func (db *_UserInfoDao) UpdateUser(ctx context.Context, dbItem *UserInfoDbItem) error {
	_, err := db.mc.UpdateByID(ctx, dbItem.UserId, bson.M{"$set": dbItem})
	return err
}

func (db *_UserInfoDao) UpdateUserState(ctx context.Context, userId string, userState uint32) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"userState": userState}})
	return err
}

func (db *_UserInfoDao) UpdateUserTestAccount(ctx context.Context, userId string, testAccount bool) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"testAccount": testAccount}})
	return err
}

func (db *_UserInfoDao) UpdateUserMemoFsId(ctx context.Context, userId, userMemoFsId string) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"userMemoFsId": userMemoFsId}})
	return err
}
